/** @类 操作类 */
export class Operator {
  constructor({ parser, observer, renderer }) {
    this.isDown = false; // 是否按下
    this.isDrag = false; // 是否拖拽
    this.isSpace = false; // 是否按下空格
    this.isDrawing = false; // 是否开启绘图
    this.downPoint = { x: 0, y: 0 }; // 按下坐标
    this.downOffset = { x: 0, y: 0 }; // 按下偏移量
    this.parser = parser;
    this.observer = observer;
    this.renderer = renderer;
    const { canvas } = renderer;
    canvas.addEventListener('mousedown', this.onMousedown.bind(this));
    canvas.addEventListener('mousemove', this.onMousemove.bind(this));
    canvas.addEventListener('mouseup', this.onMouseup.bind(this));
    canvas.addEventListener('wheel', this.onWheel.bind(this));
    canvas.addEventListener('contextmenu', (e) => e.preventDefault());
    document.addEventListener('keydown', this.onKeydown.bind(this));
    document.addEventListener('keyup', this.onKeyup.bind(this));
    document.addEventListener('mouseup', this.onMouseup.bind(this));
  }

  /** @方法 销毁监听 */
  destroy() {
    const { canvas } = this.renderer;
    canvas.removeEventListener('mousedown', this.onMousedown.bind(this));
    canvas.removeEventListener('mousemove', this.onMousemove.bind(this));
    canvas.removeEventListener('mouseup', this.onMouseup.bind(this));
    canvas.removeEventListener('wheel', this.onWheel.bind(this));
    document.removeEventListener('keydown', this.onKeydown.bind(this));
    document.removeEventListener('keyup', this.onKeyup.bind(this));
    document.removeEventListener('mouseup', this.onMouseup.bind(this));
  }

  /** @事件 按键按下 */
  onKeydown(e) {
    if (e.keyCode === 32) {
      this.isSpace = true;
    }
  }

  /** @事件 按键抬起 */
  onKeyup(e) {
    if (e.keyCode === 32) {
      this.isSpace = false;
    }
  }

  /** @事件 鼠标滚动 */
  onWheel(e) {
    this.observer.dispatch('zoom', { zoom: e.deltaY < 0 });
  }

  /** @事件 鼠标抬起 */
  onMouseup(e) {
    if (this.isDown) {
      e.stopPropagation();
      e.preventDefault();
      !this.isDrag && !this.isDrawing && this.observer.dispatch('click', { point: this.downPoint });
    }

    // 移除状态
    this.isDown = false;
    this.isDrag = false;
    this.isSpace = false;
    this.downPoint = { x: 0, y: 0 };
    this.downOffset = { x: 0, y: 0 };
  }

  /** @事件 鼠标按下 */
  onMousedown(e) {
    if (e.button === 0) {
      // 鼠标左键
      if (this.isDrawing && this.isSpace) {
        // 拖动记录偏移
        const { background } = this.parser;
        this.downOffset = { x: background.offsetX, y: background.offsetY };
        this.downPoint = this.GetPos(e);
        this.isDrag = true;
      } else if (this.isDrawing) {
        // 点击记录坐标
        this.observer.dispatch('pointdown', { point: this.GetPos(e) });
      } else {
        // 点击记录按下
        const { background } = this.parser;
        this.downOffset = { x: background.offsetX, y: background.offsetY };
        this.downPoint = this.GetPos(e);
        this.isDown = true;
      }
    } else {
      // 鼠标右键
      this.isDrawing && this.observer.dispatch('cancel');
    }
  }

  /** @事件 鼠标移动 */
  onMousemove(e) {
    if (this.isDrag || (this.isDown && !this.isDrawing)) {
      const newPoint = this.GetPos(e);
      if (newPoint.x !== this.downPoint.x || newPoint.y !== this.downPoint.y) {
        this.observer.dispatch('drag', {
          point: newPoint,
          downPoint: this.downPoint,
          downOffset: this.downOffset,
        });
      }
    } else if (this.isDrawing) {
      this.observer.dispatch('pointmove', { point: this.GetPos(e) });
    }
  }

  /** @辅助 获取点击坐标 */
  GetPos(e) {
    // 获取鼠标位置
    const { clientX, clientY } = e;
    // 获取基于画板的相对位置
    const { left, top } = this.renderer;
    // 返回缩放后的相对位置
    return {
      x: clientX - left,
      y: clientY - top,
    };
  }
}

export default Operator;
